/*
* Conditions Of Use
*
* This software was developed by employees of the Sigmatrix(Beijing) Corporation.
* This software is provided by sigmatrix as a service and is expressly
* provided "AS IS."  Sigmatrix MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
* OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
* AND DATA ACCURACY.  Sigmatrix does not warrant or make any representations
* regarding the use of the software or the results thereof, including but
* not limited to the correctness, accuracy, reliability or usefulness of
* the software.
*
* Permission to use this software is contingent upon your acceptance
* of the terms of this agreement.
*
*/
package com.sigmatrix.h5.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSONObject;
import com.sigmatrix.h5.dao.DevanningPackingBoxDao;
import com.sigmatrix.h5.dto.BaseQRCodeData;
import com.sigmatrix.h5.dto.DevanningPackingBoxData;
import com.sigmatrix.h5.dto.ProductData;
import com.sigmatrix.h5.dto.QRCodeError;
import com.sigmatrix.h5.entity.DevanningPackingBox;
import com.sigmatrix.h5.entity.FirstLevelQRCode;
import com.sigmatrix.h5.entity.MultistageLevelQRCode;
import com.sigmatrix.h5.enums.PackScanOperTypeEnum;
import com.sigmatrix.h5.enums.ResponseEnum;
import com.sigmatrix.h5.exception.DaoException;
import com.sigmatrix.h5.exception.ServiceException;
import com.sigmatrix.h5.response.Response;
import com.sigmatrix.h5.service.DevanningPackingBoxService;
import com.sigmatrix.h5.service.FirstLevelQrCodeService;
import com.sigmatrix.h5.service.MultistageLevelQrCodeService;
import com.sigmatrix.h5.service.ProductService;
import com.sigmatrix.h5.service.QrCodeService;
import com.sigmatrix.h5.service.common.impl.BaseServiceImpl;
import com.sigmatrix.h5.utils.Cons;
import com.sigmatrix.h5.web.vo.PackBoxStackVo;
import com.sigmatrix.h5.web.vo.QRCodeVo;

/**
 *@ClassName: DevanningPackingBoxServiceImpl
 *@Description: 拆组箱业务层接口
 *@author youjun
 *@date 2017-08-17 20:48:48
 */
@Service(value="devanningPackingBoxService")
public class DevanningPackingBoxServiceImpl extends BaseServiceImpl<DevanningPackingBoxData> implements DevanningPackingBoxService {

	/**logger record*/
	private static final Logger LOGGER = LoggerFactory.getLogger(DevanningPackingBoxServiceImpl.class);
	
	/**拆组箱数据库操作接口*/
	@Autowired
	private transient DevanningPackingBoxDao devanningPackingBoxDao;
	
	/**多级码业务层接口*/
	@Autowired
	private transient MultistageLevelQrCodeService multistageLevelQRCodeService;
	
	/**一级码业务层接口*/
	@Autowired
	private transient FirstLevelQrCodeService firstLevelQRCodeService;
	
	/**扫码业务层接口*/
	@Autowired
	private transient QrCodeService qRCodeService;
	
	/**产品业务层接口*/
	@Autowired
	private transient ProductService productService;

	/**(non-Javadoc)
	 *@Title: upload
	 *@Description: 
	 *@see com.sigmatrix.sc.handheld.inter.service.DevanningPackingBoxService#upload(com.sigmatrix.sc.handheld.inter.web.vo.packmanage.PackBoxStackVo)
	 *@author youjun
	 *@date 2017年8月18日 上午1:37:49
	 */
	@Transactional(rollbackFor=ServiceException.class)
	@Override
	public Response upload(final PackBoxStackVo packBoxStackVo) throws ServiceException {

		LOGGER.info("拆补箱上传操作传递参数为:{}", JSONObject.toJSONString(packBoxStackVo));
		// 参数验证  。。。。。。。
		Response validateResponse = validateUploadParams(packBoxStackVo);
		if(validateResponse.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
			return validateResponse;
		}
		try{
			final String enterpriseCode = packBoxStackVo.getEnterpriseCode();
			final String workOrder = packBoxStackVo.getWorkOrder();
			// 验证工单号是否已经存在
			if(validateIsExist(workOrder, enterpriseCode)){
				return new Response(Cons.RESPONSE_CODE_EXISTED_DATA, "工单号已存在");
			}
			
			final int operationType = packBoxStackVo.getOperationType(); // 操作方式:拆箱、组/补箱
			final String packCode = packBoxStackVo.getCode(); // 操作的包装码
			final String operateCodes = packBoxStackVo.getOperateCodes(); // 操作的包装码对应的下级码
			final String[] operateCodeArray = operateCodes.split(",");
			final List<String> operateCodeList = new ArrayList<String>();
			CollectionUtils.addAll(operateCodeList, operateCodeArray);
			final int productId = packBoxStackVo.getProductId(); // 当前操作的产品ID
			final int warehouseId = packBoxStackVo.getWarehouseId(); // 当前操作的仓库
			final Map<String, Object> map = new HashMap<String, Object>();
			final QRCodeVo qRCodeVo = new QRCodeVo();
			qRCodeVo.setCode(packCode);
			qRCodeVo.setCodeType(Cons.CODE_TYPE_CIPHER);
			qRCodeVo.setEnterpriseCode(enterpriseCode);
			// 查询操作的包装码是否符合要求
			validateResponse = qRCodeService.queryCodeByCode(qRCodeVo, map, -1);
			if(validateResponse.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
				return validateResponse;
			}
			final ProductData product = (ProductData) map.get("product");
			final int packLevel = product.getProductUnit().intValue(); // 包装单位级别
			final int codeLevel = (int) map.get("codeLevel"); // 扫码级别
			final Map<Integer, String> packStandardMap = product.getPackStandardMap();
			// 1、验证操作对象码：1、与传递的参数productId同产品2、必须为包装单位码3、验证能操作码的个数,组/补箱:剩余空位>=操作的下级码数;拆箱:当前箱具有的下级码数量>=操作的下级码数
			if(codeLevel!=packLevel) {
				return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, new StringBuilder("操作的对象码需要为")
						.append(packStandardMap.get(packLevel)).append("当前为").append(packStandardMap.get(codeLevel)).toString());
			}
			if(product.getProductId()!=productId) {
				return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, new StringBuilder("传递的产品和操作的")
						.append(packStandardMap.get(codeLevel)).append("所属产品不一致").toString());
			}
			final MultistageLevelQRCode packMultis = (MultistageLevelQRCode) map.get("multistageLevelQRCode");
			final int subNum = productService.getCountHaveSpecifiedLevelCount(product, packLevel, packLevel-1); // 包装单位具有的下级数量
			final int childNum = packMultis.getChildNum()!=null?packMultis.getChildNum():0; // 包装码现有下级码数量
			if(operationType==PackScanOperTypeEnum.GROUP_OR_FILL_BOX.getId()) { // 组/补箱:剩余空位>=操作的下级码数
				if(subNum-childNum<operateCodeArray.length){
					return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, new StringBuilder("当前组")
							.append(product.getPackUnit()).append("操作只能组")
							.append(subNum-childNum).append(packStandardMap.get(packLevel-1)).toString());
				}
			}else { // 拆箱:当前箱具有的下级码数量>=操作的下级码数
				if(StringUtils.isNotBlank(packMultis.getParentCode())) { // 拆箱操作,只能拆单独的箱(即没有上级码的)
					return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, new StringBuilder("当前拆")
							.append(product.getPackUnit()).append("操作要求所拆").append(product.getPackUnit())
							.append("不能被").append(packStandardMap.get(packLevel+1))
							.append("关联").toString());
				}
				if(childNum<operateCodeArray.length) {
					return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, new StringBuilder("当前拆")
							.append(product.getPackUnit()).append("操作最多拆").append(childNum)
							.append(packStandardMap.get(packLevel-1)).toString());
				}
			}
			// 验证组/拆箱的一系列码
			/*
			 * 2、验证所操作的包装码对应的下级码如下几点:
			 * 1)、是否为指定产品(同产品)
			 * 2)、codeLevel是否正确:即是否为包装码的下级码
			 * 3)、组/补箱的码必须是无上级码的(无上级关联关系)，拆箱的码必须是属于当前的箱码的(上级码是当前箱码)
			 */
			final List<QRCodeError> qRCodeErrorList = new ArrayList<QRCodeError>();
			validateOperateCode(enterpriseCode, qRCodeErrorList, operateCodeArray, packCode, product, operationType);
			if(!CollectionUtils.isEmpty(qRCodeErrorList)) {
				// 排除重复验证结果
				final List<QRCodeError> errorList = QRCodeError.getNotRepetition(qRCodeErrorList);
				final Map<String, Object> errorMap = new HashMap<String, Object>();
				errorMap.put("failNum", errorList.size()); // 失败总数
				errorMap.put("codeInfo", errorList); // 失败列表
				final Response errorResponse = new Response(Cons.RESPONSE_CODE_PACK_BOX_STOCK_EXCEPTION, "码信息有误", errorMap);
				LOGGER.info("码信息有误:{}", JSONObject.toJSONString(errorResponse));
				return errorResponse;
			}
			// 记录拆/补箱操作
			final DevanningPackingBox devanningPackingBox = new DevanningPackingBox();
			devanningPackingBox.setCreateTime(new Date());
			devanningPackingBox.setCurrentWrapCode(packCode);
			devanningPackingBox.setSubWrapCodes(operateCodes);
			devanningPackingBox.setNumber(operateCodeArray.length);
			final Short opreType = operationType==PackScanOperTypeEnum.GROUP_OR_FILL_BOX.getId()?Cons.PACK_BOX_FILL:Cons.PACK_BOX_SPLIT;
			devanningPackingBox.setOpreType(opreType);
			devanningPackingBox.setProductId(productId);
			devanningPackingBox.setStatus((short)1);
			devanningPackingBox.setTerminalId(packBoxStackVo.getTerminalId());
//			devanningPackingBox.setTerminalOpreId(packBoxStackVo.getUserId());
			devanningPackingBox.setWarehouseId(warehouseId);
			devanningPackingBox.setWorkOrder(workOrder);
			devanningPackingBoxDao.save(devanningPackingBox , enterpriseCode);
			// 拆组箱关系
			multistageLevelQRCodeService.packGroupSplitBox(enterpriseCode, packCode, operateCodeList, operationType);
			final Map<String, Object> resultMap = getPackGroupSplitBoxResult(enterpriseCode, packCode, product);
			return new Response(ResponseEnum.SUCCESS, resultMap);
		}catch(Exception e) {
			LOGGER.error("upload fail", e);
			throw new ServiceException(e);
		}
	}

	/**
	 *@Title: getPackGroupSplitBoxResult
	 *@Description: 获得包装组/拆箱的结果
	 *@return Map<String,Object>
	 *@author youjun
	 *@date 2017年8月21日 上午11:05:50
	 *@param enterpriseCode
	 *@param packCode
	 *@param product
	 *@return
	 * @throws ServiceException 
	 */
	private Map<String, Object> getPackGroupSplitBoxResult(final String enterpriseCode, final String packCode, 
			final ProductData product) throws ServiceException {

		if(StringUtils.isBlank(packCode)
				||StringUtils.isBlank(enterpriseCode)
				||product==null) {
			throw new ServiceException("获得拆组包装单位失败");
		}
		final Map<String, Object> map = new HashMap<String, Object>();
		final MultistageLevelQRCode packMulti = multistageLevelQRCodeService.findMultistageLevelQRCodeByQRCode(enterpriseCode, packCode, 
				Cons.CODE_TYPE_CIPHER, false);
		if(packMulti==null) {
			throw new ServiceException("包装码未找到");
		}
		final List<BaseQRCodeData> codeList = new ArrayList<BaseQRCodeData>();
		map.put("count", packMulti.getChildNum()==null?0:packMulti.getChildNum());
		final Map<Integer, String> packStandardMap = product.getPackStandardMap();
		if(packMulti.getPackLevel()>2) { // 下级码为多级码
			final MultistageLevelQRCode multistageLevelQRCodeCondition = new MultistageLevelQRCode();
			multistageLevelQRCodeCondition.setParentCode(packCode);
			multistageLevelQRCodeCondition.setEnterpriseCode(enterpriseCode);
			final List<MultistageLevelQRCode> childs = multistageLevelQRCodeService.findMultistageLevelQRCodeByCondition(multistageLevelQRCodeCondition);
			if(!CollectionUtils.isEmpty(childs)) {
				BaseQRCodeData baseQRCodeData = null;
				for(final MultistageLevelQRCode child:childs) {
					baseQRCodeData = new BaseQRCodeData();
					baseQRCodeData.setCode(child.getPackContent());
					baseQRCodeData.setFriendlyCode(child.getFriendlyCode());
					baseQRCodeData.setFriendlyCodeFront(child.getFriendlyCodeFront());
					baseQRCodeData.setFriendlyCodeEnd(child.getFriendlyCodeEnd());
					baseQRCodeData.setSeqNum(child.getSeqNum());
					baseQRCodeData.setBigUnit(packStandardMap.get(product.getBigLevel()));
					baseQRCodeData.setPackUnit(product.getPackUnit());
					baseQRCodeData.setPackUnderUnit(packStandardMap.get(product.getProductUnit()-1));
					codeList.add(baseQRCodeData);
				}
			}
		}else{ // 为单品码
			final FirstLevelQRCode firstLevelQRCodeCondition = new FirstLevelQRCode();
			firstLevelQRCodeCondition.setEnterpriseCode(enterpriseCode);
			firstLevelQRCodeCondition.setParentCode(packCode);
			final List<FirstLevelQRCode> childs = firstLevelQRCodeService.findFirstLevelQRCodeListByCondition(firstLevelQRCodeCondition);
			if(!CollectionUtils.isEmpty(childs)) {
				BaseQRCodeData baseQRCodeData = null;
				for(final FirstLevelQRCode child:childs) {
					baseQRCodeData = new BaseQRCodeData();
					baseQRCodeData.setCode(child.getQrCode());
					baseQRCodeData.setFriendlyCode(child.getFriendlyCode());
					baseQRCodeData.setFriendlyCodeFront(child.getFriendlyCodeFront());
					baseQRCodeData.setFriendlyCodeEnd(child.getFriendlyCodeEnd());
					baseQRCodeData.setSeqNum(child.getSeqNum());
					baseQRCodeData.setBigUnit(packStandardMap.get(product.getBigLevel()));
					baseQRCodeData.setPackUnit(product.getPackUnit());
					baseQRCodeData.setPackUnderUnit(packStandardMap.get(product.getProductUnit()-1));
					codeList.add(baseQRCodeData);
				}
			}
		}
		map.put("codeList", codeList);
		return map;
	}

	/**
	 *@Title: validateOperateCode
	 *@Description: 验证所操作的包装码对应的下级码如下几点:<br/>
			 * 1)、是否为指定产品(同产品)<br/>
			 * 2)、codeLevel是否正确:即是否为包装码的下级码<br/>
			 * 3)、组/补箱的码必须是无上级码的(无上级关联关系)，拆箱的码必须是属于当前的箱码的(上级码是当前箱码)<br/>
	 *@return void
	 *@author youjun
	 *@date 2017年8月18日 下午3:50:00
	 *@param enterpriseCode
	 *@param qRCodeErrorList
	 *@param operateCodeArray
	 *@param product 产品信息
	 *@param operationType
	 * @throws ServiceException 
	 */
	private void validateOperateCode(final String enterpriseCode, final List<QRCodeError> qRCodeErrorList,
			final String[] operateCodeArray, final String packCode, final ProductData product, 
			final int operationType) throws ServiceException {

		if(StringUtils.isBlank(enterpriseCode)
				||qRCodeErrorList==null
				||operateCodeArray==null) {
			throw new ServiceException("验证所操作的包装码对应的下级码参数错误");
		}
		final int productId = product.getProductId();
		final int packLevel = product.getProductUnit().intValue();
		final String packUnit = product.getPackUnit(); // 包装单位
		final Map<Integer, String> packStandardMap = product.getPackStandardMap();
		QRCodeVo qRCodeVo = null;
		QRCodeError qRCodeError = null;
		StringBuilder errorMsg = null;
		for(final String operateCode:operateCodeArray) {
			final Map<String, Object> map = new HashMap<String, Object>();
			qRCodeVo = new QRCodeVo(enterpriseCode, operateCode, Cons.CODE_TYPE_CIPHER);
			final Response response = qRCodeService.queryCodeByCode(qRCodeVo, map, -1);
			if(response.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
				qRCodeError = new QRCodeError(operateCode, null, productId, response.getMessage());
			}else{
				final String parentCode = (String) map.get("parentCode"); // 上级码
				final int codeLevel = (int) map.get("codeLevel");
				final int codeProductId = product.getProductId();
				// 是否为指定产品(同产品)
				if(productId!=product.getProductId()) {
					errorMsg = new StringBuilder("不属于当前产品");
					qRCodeError = new QRCodeError(operateCode, codeLevel, codeProductId, errorMsg.toString());
				}else if(codeLevel+1!=packLevel){ // codeLevel是否正确
					errorMsg = new StringBuilder("当前为").append(packStandardMap.get(codeLevel));
					errorMsg.append("，请扫").append(packStandardMap.get(packLevel-1));
					qRCodeError = new QRCodeError(operateCode, codeLevel, codeProductId, errorMsg.toString());
				}else{ // 组/补箱的码必须是无上级码的(无上级关联关系)且下级码是满的，拆箱的码必须是属于当前的箱码的(上级码是当前箱码)
					if(operationType==PackScanOperTypeEnum.GROUP_OR_FILL_BOX.getId()) { // 组/补箱
						if(StringUtils.isNotBlank(parentCode)) {
							errorMsg = new StringBuilder("当前").append(packStandardMap.get(codeLevel));
							errorMsg.append("存在").append(packStandardMap.get(codeLevel));
							errorMsg.append(product.getPackUnit()).append("关联");
							qRCodeError = new QRCodeError(operateCode, codeLevel, codeProductId, errorMsg.toString());
						}else if(codeLevel>1) { // 仅组/补箱操作(拆箱操作不判断)时判断操作码（非单品码时）是否满包装
							final MultistageLevelQRCode multis = (MultistageLevelQRCode) map.get("multistageLevelQRCode");
							qRCodeError =  multistageLevelQRCodeService.validateCodeIsFullPack(multis, product);
						}
					}else if(operationType==PackScanOperTypeEnum.SPLIT_BOX.getId()
							&&!parentCode.equals(packCode)) {
						errorMsg = new StringBuilder("当前码非指定").append(packUnit).append("的")
								.append(packStandardMap.get(codeLevel)).append("码");
						qRCodeError = new QRCodeError(operateCode, codeLevel, codeProductId, errorMsg.toString());
					}
				}
			}
			if(qRCodeError!=null) {
				qRCodeErrorList.add(qRCodeError); // 加入错误列表
			}
		}
	}

	/**
	 *@Title: validateIsExist
	 *@Description: 验证工单号是否存在
	 *@return boolean
	 *@author youjun
	 *@date 2017年8月18日 上午1:48:57
	 *@param workOrder
	 *@return
	 * @throws ServiceException 
	 */
	private boolean validateIsExist(final String workOrder, final String enterpriseCode) throws ServiceException {

		if(StringUtils.isNotBlank(workOrder)) {
			final DevanningPackingBox devanningPackingBoxCondition = new DevanningPackingBox();
			devanningPackingBoxCondition.setWorkOrder(workOrder);
			try {
				final List<DevanningPackingBox> list = devanningPackingBoxDao.findListByCondition(devanningPackingBoxCondition, enterpriseCode);
				if(!CollectionUtils.isEmpty(list)) {
					return true; // 代表已存在
				}
			} catch (DaoException e) {
				throw new ServiceException(e);
			}
		}
		return false;
	}

	/**
	 *@Title: validateUploadParams
	 *@Description: TODO()
	 *@return Response
	 *@author youjun
	 *@date 2017年8月18日 上午1:46:58
	 *@param packBoxStackVo
	 *@return
	 */
	private Response validateUploadParams(final PackBoxStackVo packBoxStackVo) {

		final int operationType = packBoxStackVo.getOperationType();
		if(operationType!=PackScanOperTypeEnum.GROUP_OR_FILL_BOX.getId()
				&&operationType!=PackScanOperTypeEnum.SPLIT_BOX.getId()) {
			return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "对包装单位不支持此操作");
		}
		return new Response(ResponseEnum.SUCCESS);
	}
	
	
}
